package com.superhelper.app.service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.superhelper.app.common.Consts;
import com.superhelper.app.domain.AccessToken;
import com.superhelper.app.mapper.AdminMapper;
import com.superhelper.common.domain.AuserShop;
import com.superhelper.common.domain.TakeOutOrderStatus;
import com.superhelper.common.domain.TakeOutShopInfo;
import com.superhelper.common.service.RedisService;
import com.superhelper.common.utils.JsonHelper;

import eleme.openapi.sdk.api.entity.order.OOrder;
import eleme.openapi.sdk.api.entity.shop.OShop;
import eleme.openapi.sdk.api.enumeration.order.OInvalidateType;
import eleme.openapi.sdk.api.enumeration.shop.OShopProperty;
import eleme.openapi.sdk.api.exception.ServiceException;
import eleme.openapi.sdk.api.exception.UnauthorizedException;
import eleme.openapi.sdk.api.service.OrderService;
import eleme.openapi.sdk.api.service.ShopService;
import eleme.openapi.sdk.api.service.UserService;
import eleme.openapi.sdk.config.Config;
import eleme.openapi.sdk.oauth.OAuthClient;
import eleme.openapi.sdk.oauth.response.Token;

@Service
public class ElemeService {
    protected Logger log = LoggerFactory.getLogger(this.getClass());
    @Value("${eleme.app_key}")
    private String eleme_app_key;
    @Value("${eleme.app_secret}")
    private String eleme_app_secret;
    @Value("${eleme.callback_url}")
    private String eleme_callback_url;
    @Value("${eleme.send_box}")
    private boolean eleme_send_box;
    @Autowired
    private RedisService redisService;
    @Autowired
    private AdminMapper adminMapper;
    private ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 200, 1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>());

    public OAuthClient getOAuthClient() {
        // 实例化一个配置类
        Config config = getConfig();
        // 使用config对象，实例化一个授权类
        return new OAuthClient(config);
    }

    public Config getConfig() {
        // 实例化一个配置类
        return new Config(eleme_send_box, eleme_app_key, eleme_app_secret);
    }

    public String getAuthUrl(Integer auserId) {
        String scope = "all";
        String state = String.valueOf(auserId);
        return this.getOAuthClient().getAuthUrl(eleme_callback_url, scope, state);
    }

    public Token getTokenByCode(String authCode) {
        return this.getOAuthClient().getTokenByCode(authCode, eleme_callback_url);
    }

    public String getShopId(Token token) throws ServiceException {
        UserService userService = new UserService(this.getConfig(), token);
        Long shopId = userService.getUser().getAuthorizedShops().get(0).getId();
        return String.valueOf(shopId);
    }

    public Token getTokenByRefreshToken(String refreshToken) {
        return this.getOAuthClient().getTokenByRefreshToken(refreshToken);
    }

    public TakeOutShopInfo getShopInfo(String shopId) throws ServiceException {
        Token token = this.getToken(shopId);
        if (token != null) {
            ShopService shopService = new ShopService(this.getConfig(), token);
            OShop shop = null;
            try {
                shop = shopService.getShop(Long.parseLong(shopId));
            } catch (UnauthorizedException e) {
                if (e.getMessage().contains("token认证失败")) {
                    token = this.getTokenByRefreshToken(token.getRefreshToken());
                    String token_key = Consts.Cache.ELEME_TOKEN + shopId;
                    AccessToken accessToken = new AccessToken();
                    accessToken.setExpires(token.getExpires());
                    accessToken.setRefreshToken(token.getRefreshToken());
                    accessToken.setTokenType(token.getTokenType());
                    accessToken.setToken(token.getAccessToken());
                    accessToken.setTokenAt(new Date());
                    accessToken.setShopId(shopId);
                    adminMapper.reflushShopToken(shopId, AuserShop.SHOP_ELEME, JsonHelper.toJson(accessToken));
                    redisService.set(token_key, accessToken);
                    shopService = new ShopService(this.getConfig(), token);
                    shop = shopService.getShop(Long.parseLong(shopId));
                }
            }
            TakeOutShopInfo info = new TakeOutShopInfo();
            info.setShop_type(AuserShop.SHOP_ELEME);
            info.setePoiId(shopId);
            info.setPictureUrl(shop.getImageUrl());
            info.setName(shop.getName());
            info.setAddress(shop.getAddressText());
            if (shop.getPhones().size() > 0) {
                info.setPhone(shop.getPhones().get(0));
            }
            info.setIsOpen(shop.getIsOpen());
            info.setLatitude(shop.getLatitude());
            info.setLongitude(shop.getLongitude());
            return info;
        }
        return null;
    }

    /**
     * 授权码(企业)模式刷新Token
     * 
     * @throws ServiceException
     */
    public Token getToken(String shopId) throws ServiceException {
        // 后期优化，放缓存token
        String token_key = Consts.Cache.ELEME_TOKEN + shopId;
        AccessToken accessToken = redisService.get(token_key, AccessToken.class);
        if (accessToken != null) {
            long time = System.currentTimeMillis() / 1000;
            long tokenFlashTime = accessToken.getTokenAt().getTime() / 1000;
            if (time - tokenFlashTime >= (accessToken.getExpires() - 5 * 60)) {// token
                Token token = this.getTokenByRefreshToken(accessToken.getRefreshToken()); // 过期，提前5分钟
                if (token.isSuccess()) {
                    accessToken = new AccessToken();
                    accessToken.setExpires(token.getExpires());
                    accessToken.setRefreshToken(token.getRefreshToken());
                    accessToken.setTokenType(token.getTokenType());
                    accessToken.setToken(token.getAccessToken());
                    accessToken.setTokenAt(new Date());
                    accessToken.setShopId(shopId);
                    adminMapper.reflushShopToken(shopId, AuserShop.SHOP_ELEME, JsonHelper.toJson(accessToken));
                    redisService.set(token_key, accessToken);
                    return token;
                } else {
                    throw new ServiceException(token.getError(), token.getError_description());
                }
            } else {

                Token token = new Token();
                token.setAccessToken(accessToken.getToken());
                token.setExpires(accessToken.getExpires());
                token.setRefreshToken(accessToken.getRefreshToken());
                token.setTokenType(accessToken.getTokenType());
                return token;
            }
        } else {
            AuserShop s = adminMapper.getAuserShopByType(shopId, AuserShop.SHOP_ELEME);
            accessToken = JsonHelper.fromJson(s.getToken(), AccessToken.class);
            redisService.set(token_key, accessToken);

            Token token = new Token();
            token.setAccessToken(accessToken.getToken());
            token.setExpires(accessToken.getExpires());
            token.setRefreshToken(accessToken.getRefreshToken());
            token.setTokenType(accessToken.getTokenType());
            return token;
        }
    }

    public boolean confirmOrder(final String shopId, final String orderId) throws ServiceException {

        threadPool.submit(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    long time = System.currentTimeMillis();
                    Token token = getToken(shopId);
                    if (token != null) {
                        OrderService orderService = new OrderService(getConfig(), token);
                        orderService.confirmOrderLite(orderId);
                        long t = System.currentTimeMillis() - time;
                        log.info("ele->confirmOrder cost:{},result:{}", t, true);
                    }
                } catch (Exception e) {
                    log.error("{}", e);
                }
            }
        });

        return true;

    }

    public boolean cancelOrder(String shopId, String orderId) throws ServiceException {

        Token token = this.getToken(shopId);
        if (token != null) {
            OrderService orderService = new OrderService(this.getConfig(), token);
            orderService.cancelOrderLite(orderId, OInvalidateType.others, "");
            log.info("cancelOrder result:{}", true);
        }
        return true;

    }

    public boolean agreeRefund(String shopId, String orderId) throws ServiceException {

        Token token = this.getToken(shopId);
        if (token != null) {
            OrderService orderService = new OrderService(this.getConfig(), token);
            OOrder order = orderService.agreeRefundLite(orderId);
            log.info("agreeRefund result:{}", JsonHelper.toJson(order));
        }
        return true;
    }

    public boolean disagreeRefund(String shopId, String orderId) throws ServiceException {

        Token token = this.getToken(shopId);
        if (token != null) {
            OrderService orderService = new OrderService(this.getConfig(), token);
            OOrder order = orderService.disagreeRefundLite(orderId, "");
            log.info("disagreeRefund result:{}", JsonHelper.toJson(order));
        }
        return true;
    }

    public boolean openShop(String shopId, Integer state) throws ServiceException {
        Token token = this.getToken(shopId);
        if (token != null) {
            ShopService shopService = new ShopService(this.getConfig(), token);
            Map<OShopProperty, Object> properties = new HashMap<OShopProperty, Object>();
            properties.put(OShopProperty.isOpen, state);
            OShop shop = shopService.updateShop(Long.parseLong(shopId), properties);
            log.info("openShop result:{}", JsonHelper.toJson(shop));
            return true;
        }
        return false;
    }

    public String getOrderStatus(String shopId, String orderId) throws ServiceException {

        Token token = this.getToken(shopId);
        if (token != null) {
            OrderService orderService = new OrderService(this.getConfig(), token);
            OOrder order = orderService.getOrder(orderId);
            log.info("getOrderStatus result:{}", JsonHelper.toJson(order));
            if (order.getRefundStatus().name().equals("rejected")) {
                return TakeOutOrderStatus.SETTLED.getValue();
            } else {
                return order.getStatus().name();
            }
        }
        return null;
    }

}
